home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #2 / Ham Radio 2000 - Volume 2.iso / HAMV2 / TCP_IP / TNOS230S / ICMPCMD.C < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-18  |  10.4 KB  |  429 lines

  1. /* ICMP-related user commands
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4. /* Mods by PA0GRI */
  5. #include "global.h"
  6. #include "commands.h"
  7. #ifndef MSDOS
  8. #include <time.h>
  9. #endif
  10. #include "icmp.h"
  11.  
  12. #if !defined(_lint)
  13. static char rcsid[] OPTIONAL = "$Id: icmpcmd.c,v 1.20 1997/08/19 01:19:22 root Exp root $";
  14. #endif
  15.  
  16. static int pingcmd (int argc, char *argv[], void *p);
  17. static int doicmpec (int argc, char *argv[], void *p);
  18. static int doicmpstat (int argc, char *argv[], void *p);
  19. static int doicmptr (int argc, char *argv[], void *p);
  20. static int doicmptimeexceed (int argc, char *argv[], void *p);
  21. static void pingtx (int s, void *ping1, void *p);
  22. static void pinghdr (struct session * sp, struct ping * ping, time_t * start);
  23.  
  24. #ifdef CATALOG
  25. #include "catalog.h"
  26.  
  27. #define CAT icmpcmd_catalog
  28.  
  29. #define tracingis    __STR(0)
  30. #define tracemodes    __STR(1)
  31. #define echostr        __STR(2)
  32. #define ttlstr        __STR(3)
  33. #define resolving    __STR(4)
  34. #define pinging        __STR(5)
  35. #define pingaborted    __STR(6)
  36. #define pingtimeout    __STR(7)
  37. #define pinghdrstr1    __STR(8)
  38. #define pinghdrstr2    __STR(9)
  39.  
  40. #else /* CATALOG */
  41. static const char tracingis[] = "ICMP Tracing is %d\n";
  42. static const char tracemodes[] = "Trace modes are: 0|1|2\n";
  43. static const char echostr[] = "ICMP echo response accept";
  44. static const char ttlstr[] = "Ttl time exceed reply";
  45. static const char resolving[] = "Resolving %s... ";
  46. static const char pinging[] = "Pinging %s... Press <CR> to abort...\n";
  47. static const char pingaborted[] = "%s: ping aborted by user after %d seconds\n";
  48. static const char pingtimeout[] = "%s: no response after 120 seconds\n";
  49. static const char pinghdrstr1[] = "Pinging %s (%s); data %d interval %lu ms:\n    since %s";
  50. static const char pinghdrstr2[] = "      sent       rcvd     %%        rtt    avg rtt       mdev     received\n";
  51.  
  52. #endif /* CATALOG */
  53.  
  54.  
  55. static const char statstr1[] = "(%2u)%-20s%10lu";
  56. static const char statstr2[] = "     (%2u)%-20s%10lu\n";
  57. static const char pingfmt[] = "%10lu %10lu %5lu %10lu %10lu %10lu     %8.8s\n";
  58. static const char pingrtt[] = "%s: rtt %lu\n";
  59.  
  60. static int currentPing = 0;
  61.  
  62.  
  63. static struct cmds Icmpcmds[] =
  64. {
  65.     { "echo",    doicmpec,        0, 0, NULLCHAR},
  66.     { "status",    doicmpstat,        0, 0, NULLCHAR},
  67.     { "timeexceed",    doicmptimeexceed,    0, 0, NULLCHAR},
  68.     { "trace",    doicmptr,        0, 0, NULLCHAR},
  69.     { NULLCHAR,    NULL,            0, 0, NULLCHAR},
  70. };
  71.  
  72.  
  73. int Icmp_trace;
  74. static int Icmp_echo = 1;
  75.  
  76.  
  77. int
  78. doicmp (argc, argv, p)
  79. int argc;
  80. char *argv[];
  81. void *p;
  82. {
  83.     return subcmd (Icmpcmds, argc, argv, p);
  84. }
  85.  
  86.  
  87. static int
  88. doicmpstat (argc, argv, p)
  89. int argc OPTIONAL;
  90. char *argv[] OPTIONAL;
  91. void *p OPTIONAL;
  92. {
  93. register int i;
  94. int lim;
  95.  
  96.     /* Note that the ICMP variables are shown in column order, because
  97.      * that lines up the In and Out variables on the same line
  98.      */
  99.     lim = NUMICMPMIB / 2;
  100.     for (i = 1; i <= lim; i++) {
  101.         tprintf (statstr1, i, Icmp_mib[i].name, Icmp_mib[i].value.integer);
  102.         tprintf (statstr2, i + lim, Icmp_mib[i + lim].name, Icmp_mib[i + lim].value.integer);
  103.     }
  104.     return 0;
  105. }
  106.  
  107.  
  108. static int
  109. doicmptr (argc, argv, p)
  110. int argc;
  111. char *argv[];
  112. void *p OPTIONAL;
  113. {
  114.     if (argc < 2) {
  115.         tprintf (tracingis, Icmp_trace);
  116.         return 0;
  117.     }
  118.     switch (argv[1][0]) {
  119.         case '0':
  120.         case '1':
  121.         case '2':
  122.             Icmp_trace = atoi (argv[1]);
  123.             break;
  124.         default:
  125.             tputs (tracemodes);
  126.             return -1;
  127.     }
  128.  
  129.     return 0;
  130. }
  131.  
  132.  
  133. static int
  134. doicmpec (argc, argv, p)
  135. int argc;
  136. char *argv[];
  137. void *p OPTIONAL;
  138. {
  139.     return setbool (&Icmp_echo, echostr, argc, argv);
  140. }
  141.  
  142.  
  143. int Icmp_timeexceed = 1;
  144. static int
  145. doicmptimeexceed (argc, argv, p)
  146. int argc;
  147. char *argv[];
  148. void *p OPTIONAL;
  149. {
  150.     return setbool (&Icmp_timeexceed, ttlstr, argc, argv);
  151. }
  152.  
  153.  
  154. /* Send ICMP Echo Request packets */
  155. static int
  156. pingcmd (argc, argv, p)
  157. int argc;
  158. char *argv[];
  159. void *p OPTIONAL;
  160. {
  161. struct proc *pinger = NULLPROC;    /* Transmit process */
  162. struct sockaddr_in from;
  163. struct icmp icmp;
  164. struct mbuf *bp;
  165. int32 timestamp, rtt, abserr;
  166. int s, fromlen;
  167. struct ping ping;
  168. struct session *sp = (struct session *) 0;
  169. time_t starttime, thetime;
  170. char *cptr;
  171. int usesession = 0;
  172.  
  173.     /* Make sure this is a 'one-shot- ping
  174.      * if not coming from the console - WG7J
  175.      */
  176.     if (argc > 3)
  177.         if (Curproc->input != Command->input)
  178.             return 0;
  179.  
  180.     memset ((char *) &ping, 0, sizeof (ping));
  181.     /* Allocate a session descriptor if this comes from console */
  182.     if (Curproc->input == Command->input) {
  183.         usesession = 1;
  184.         if ((sp = ping.sp = newsession (argv[1], PING, 0)) == NULLSESSION) {
  185.             tputs (TooManySessions);
  186.             return 1;
  187.         }
  188.     }
  189.     if ((s = socket (AF_INET, SOCK_RAW, ICMP_PTCL)) == -1) {
  190.         tputs (Nosock);
  191.         if (usesession) {
  192.             (void) keywait (NULLCHAR, 1);
  193.             freesession (sp);
  194.         }
  195.         return 1;
  196.     }
  197.     if (usesession && sp != NULLSESSION)
  198.         sp->s = s;
  199.  
  200.     tprintf (resolving, argv[1]);
  201.  
  202.     if ((ping.target = resolve (argv[1])) == 0) {
  203.         tprintf (Badhost, argv[1]);
  204.         if (usesession) {
  205.             (void) keywait (NULLCHAR, 1);
  206.             freesession (sp);
  207.         }
  208.         return 1;
  209.     }
  210.     if (argc > 2)
  211.         ping.len = (int16) atoi (argv[2]);
  212.  
  213.     if (argc > 3)
  214.         ping.interval = atol (argv[3]);
  215.  
  216.  
  217.     /* Optionally ping a range of IP addresses */
  218.     if (argc > 4)
  219.         ping.incflag = 1;
  220.  
  221.     if (ping.interval != 0)
  222.         pinger = newproc ("pingtx", 300, pingtx, s, &ping, NULL, 0);
  223.     else {
  224.         /* One shot ping; let echo_proc hook handle response.
  225.          * An ID of MAXINT16 will not be confused with a legal socket
  226.          * number, which is used to identify repeated pings
  227.          */
  228.         if (usesession) {
  229.             pingem (s, ping.target, 0, MAXINT16, ping.len);
  230.             freesession (sp);
  231.         } else {
  232.             int k;
  233.  
  234.             tprintf (pinging, argv[1]);
  235.             usflush (Curproc->output);
  236.             currentPing = Curproc->output;
  237.             pingem (s, ping.target, 0, MAXINT16, ping.len);
  238.             for (k = 0; k < 120; k++) {
  239.                 if (!currentPing)
  240.                     break;
  241.                 if (socklen (Curproc->input, 0)) {
  242.                     (void) recv_mbuf (Curproc->input, NULL, 0, NULLCHAR, 0);    /* flush */
  243.                     tprintf (pingaborted, argv[1], k);
  244.                     break;
  245.                 }
  246.                 (void) kpause (1000L);
  247.             }
  248.             currentPing = 0;
  249.             if (k == 120)
  250.                 tprintf (pingtimeout, argv[1]);
  251.             close_s (s);
  252.         }
  253.         return 0;
  254.     }
  255.     (void) time (&starttime);
  256.     /* Now collect the replies */
  257.     pinghdr (sp, &ping, &starttime);
  258.     for ( ; ; ) {
  259.         fromlen = sizeof (from);
  260.         if (recv_mbuf (s, &bp, 0, (char *) &from, &fromlen) == -1)
  261.             break;
  262.         (void) ntohicmp (&icmp, &bp);
  263.         if (icmp.type != ICMP_ECHO_REPLY || icmp.args.echo.id != s) {
  264.             /* Ignore other people's responses */
  265.             free_p (bp);
  266.             continue;
  267.         }
  268.         /* Get stamp */
  269.         if (pullup (&bp, (unsigned char *) ×tamp, sizeof (timestamp))
  270.             != sizeof (timestamp)) {
  271.             /* The timestamp is missing! */
  272.             free_p (bp);    /* Probably not necessary */
  273.             continue;
  274.         }
  275.         free_p (bp);
  276.  
  277.         ping.responses++;
  278.  
  279.         /* Compute round trip time, update smoothed estimates */
  280.         rtt = msclock () - timestamp;
  281.         if (rtt < 0)
  282.             rtt = 0;
  283.         abserr = (rtt > ping.srtt) ? (rtt - ping.srtt) : (ping.srtt - rtt);
  284.  
  285.         if (ping.responses == 1) {
  286.             /* First response, base entire SRTT on it */
  287.             ping.srtt = rtt;
  288.             ping.mdev = 0;
  289.         } else {    /*lint -save -e704 */
  290.             ping.srtt = (7 * ping.srtt + rtt + 4) >> 3;
  291.             ping.mdev = (3 * ping.mdev + abserr + 2) >> 2;
  292.         }        /*lint -restore */
  293.         if ((ping.responses % 20) == 0)
  294.             pinghdr (sp, &ping, &starttime);
  295.         (void) time (&thetime);
  296.         cptr = ctime (&thetime);
  297.         tprintf (pingfmt, ping.sent, ping.responses,
  298.              (ping.responses * 100 + ping.sent / 2) / ping.sent,
  299.              rtt, ping.srtt, ping.mdev, &cptr[11]);
  300.     }
  301.     if (pinger != NULLPROC)
  302.         killproc (pinger);
  303.     freesession (sp);
  304.     return 0;
  305. }
  306.  
  307.  
  308. int
  309. doping (argc, argv, p)
  310. int argc;
  311. char *argv[];
  312. void *p;
  313. {
  314. char **pargv;
  315. int i;
  316.  
  317.     if (Curproc->input == Command->input) {
  318.         /* Make private copy of argv and args,
  319.          * spawn off subprocess and return.
  320.          */
  321.         pargv = (char **) callocw ((unsigned) argc + 1, sizeof (char *));
  322.  
  323.         for (i = 0; i < argc; i++)
  324.             pargv[i] = strdup (argv[i]);
  325.         pargv[i] = NULL;
  326.         (void) newproc ("ping", 512, (void (*)(int, void *, void *)) pingcmd, argc, (void *) pargv, p, 1);
  327.     } else
  328.         (void) pingcmd (argc, argv, p);
  329.     return 0;
  330. }
  331.  
  332.  
  333. static void
  334. pinghdr (sp, ping, start)
  335. struct session *sp;
  336. struct ping *ping;
  337. time_t *start;
  338. {
  339.     tprintf (pinghdrstr1, sp->name, inet_ntoa (ping->target), ping->len, ping->interval, ctime (start));
  340.     tprintf (pinghdrstr2);
  341. }
  342.  
  343.  
  344. void
  345. echo_proc (source, dest, icmp, bp)
  346. uint32 source;
  347. uint32 dest OPTIONAL;
  348. struct icmp *icmp;
  349. struct mbuf *bp;
  350. {
  351. int32 timestamp, rtt;
  352.  
  353.     if (Icmp_echo && icmp->args.echo.id == MAXINT16
  354.         && pullup (&bp, (unsigned char *) ×tamp, sizeof (timestamp))
  355.         == sizeof (timestamp)) {
  356.         /* Compute round trip time */
  357.         rtt = msclock () - timestamp;
  358.         usprintf ((currentPing) ? currentPing : Command->output, pingrtt, inet_ntoa (source), rtt);
  359.         currentPing = 0;
  360.     }
  361.     free_p (bp);
  362. }
  363.  
  364.  
  365. /* Ping transmit process. Runs until killed */
  366. static void
  367. pingtx (s, ping1, p)
  368. int s;                /* Socket to use */
  369. void *ping1;
  370. void *p OPTIONAL;
  371. {
  372. struct ping *ping;
  373.  
  374.     ping = (struct ping *) ping1;
  375.     ping->sent = 0;
  376.     if (ping->incflag) {
  377.         for (;;) {
  378.             pingem (s, ping->target++, 0, MAXINT16, ping->len);
  379.             ping->sent++;
  380.             (void) kpause (ping->interval);
  381.         }
  382.     } else {
  383.         for (;;) {
  384.             pingem (s, ping->target, (int16) ping->sent++, (int16) s, ping->len);
  385.             (void) kpause (ping->interval);
  386.         }
  387.     }
  388. }
  389.  
  390.  
  391. /* Send ICMP Echo Request packet */
  392. void
  393. pingem (s, target, seq, id, len)
  394. int s;                /* Raw socket on which to send ping */
  395. uint32 target;            /* Site to be pinged */
  396. int16 seq;            /* ICMP Echo Request sequence number */
  397. int16 id;            /* ICMP Echo Request ID */
  398. int16 len;            /* Length of optional data field */
  399. {
  400. struct mbuf *data;
  401. struct mbuf *bp;
  402. struct icmp icmp;
  403. struct sockaddr_in to;
  404. int32 theclock;
  405.  
  406.     theclock = msclock ();
  407.     data = ambufw ((int16) (len + sizeof (theclock)));
  408.     data->cnt = len + sizeof (theclock);
  409.     /* Set optional data field, if any, to all 55's */
  410.     if (len != 0)
  411.         memset (data->data + sizeof (theclock), 0x55, (size_t) len);
  412.  
  413.     /* Insert timestamp and build ICMP header */
  414.     memcpy (data->data, (char *) &theclock, sizeof (theclock));
  415.     icmpOutEchos++;
  416.     icmpOutMsgs++;
  417.     icmp.type = ICMP_ECHO;
  418.     icmp.code = 0;
  419.     icmp.args.echo.seq = seq;
  420.     icmp.args.echo.id = id;
  421.     if ((bp = htonicmp (&icmp, data)) == NULLBUF) {
  422.         free_p (data);
  423.         return;
  424.     }
  425.     to.sin_family = AF_INET;
  426.     to.sin_addr.s_addr = target;
  427.     (void) send_mbuf (s, bp, 0, (char *) &to, sizeof (to));
  428. }
  429.